package slap.mvc.result;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jodd.io.FileNameUtil;
import jodd.io.StreamUtil;
import jodd.io.findfile.FilepathScanner;

import slap.Resources;
import slap.Slap;
import slap.app.Application;
import slap.cache.Cache;
import slap.cache.CacheManager;
import slap.http.Header;
import slap.http.Request;
import slap.http.Response;
import slap.util.GZIP;
import slap.util.MimeTypes;
import slap.util.Time;
import slap.util.Utils;

public class RenderStatic extends Result {
	
	protected static final Logger logger = LoggerFactory.getLogger(RenderStatic.class);
	
	private static final String REALM_STATIC_PLAIN = "static-plain";
	private static final String REALM_STATIC_GZIP = "static-gzip";
	
	private static Cache cache = CacheManager.getCache();
	
	protected static Map<String, Long> static_files = new HashMap<String, Long>();
    private static List<String> allowGzip = new ArrayList<String>();
	private static List<String> disallow = new ArrayList<String>();
	
	public static void init() {
		allowGzip.add("js");
		allowGzip.add("css");
		allowGzip.add("html");
		disallow.add("vm");
		logger.debug("Adding static cache...");
		prepareStaticCache();
	}
    
	private static void prepareStaticCache(){
		FilepathScanner scanner = new FilepathScanner(){
			String key;
			@Override
			protected void onFile(File entity) {
				if(disallow.contains(FileNameUtil.getExtension(entity.getName()) )){
					return;
				}
				String up = entity.getAbsolutePath().replace('\\', '/');
				key = up.substring(up.indexOf(Resources.Presets.DIR_STATIC)+Resources.Presets.DIR_STATIC.length()-1);
				byte[] b = null;
				try {
					b = StreamUtil.readBytes(new FileInputStream(entity) );
				} catch (FileNotFoundException e) {
					e.printStackTrace();
					return;
				} catch (IOException e) {
					e.printStackTrace();
					return;
				}
				if(allowGzip.contains(FileNameUtil.getExtension(key) )){
					try {
						cache.put(REALM_STATIC_GZIP, key, GZIP.compress(b));
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				cache.put(REALM_STATIC_PLAIN, key, b);
				static_files.put(key, entity.lastModified());
			}
		};
		
		scanner.includeDirs(false).setRecursive(true);
		scanner.scan(new File(Resources.Presets.DIR_STATIC));
	}
	
	public String file;
	public FileInputStream input;
	public Long lastModified;
	public Application app;
	
    public RenderStatic(String file, Application app) {
        this.file = file;
        this.app = app;
        if(app.debug){
        	try {
            	File sf = new File(Resources.Presets.DIR_STATIC+file);
            	lastModified = sf.lastModified();
    			this.input = new FileInputStream(sf);
    		} catch (FileNotFoundException e) {
    			logger.info(e.getMessage());
    			throw new NotFound(file);
    		}
        }else if((lastModified = static_files.get(file))==null){
        	throw new NotFound(file);
        }
        if(!Request.current().isModified(lastModified)){
    		throw new NotModified();
    	}
    }

    @Override
    public void apply(Request request, Response response) {
    	String ext = FileNameUtil.getExtension(file);
    	response.contentType = MimeTypes.getContentType(ext);
        
    	if(!app.debug){
    		byte[] content = null;
    		if(request.acceptGzip() && allowGzip.contains(ext)){
    			content = cache.get(REALM_STATIC_GZIP, file, byte[].class);
    			response.enableGZipTransfer();
    		}else{
    			content = cache.get(REALM_STATIC_PLAIN, file, byte[].class);
    		}
    		
    		if(content == null || content.length == 0){
				throw new Error(file);
			}
    		try {
				response.out.write(content);
			} catch (IOException e) {
				e.printStackTrace();
			}
			logger.debug("Getting static in cache.");
    	}else{
    		try {
        		if(request.acceptGzip() && allowGzip.contains(FileNameUtil.getExtension(file))){
        			GZIP.compress(input, response.out);
        			response.enableGZipTransfer();
        		}else{
        			StreamUtil.copy(input, response.out);
        		}
    			input.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    	}
    	
		response.cacheFor("3d");
		response.headers.put("Last-Modified", new Header("Last-Modified", Time.httpFormatter.format(new Date(lastModified))));
    }

}
